package com.ants.modules.system.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ants.common.annotation.AutoLog;
import com.ants.common.constant.CommonConstant;
import com.ants.common.system.api.ISysBaseAPI;
import com.ants.common.system.result.Result;
import com.ants.common.utils.*;
import com.ants.modules.system.entity.SysRole;
import com.ants.modules.system.entity.SysUser;
import com.ants.modules.system.entity.SysUserRole;
import com.ants.modules.system.model.SysLoginModel;
import com.ants.modules.system.service.*;
import com.ants.modules.system.service.impl.SysBaseApiImpl;
import com.ants.modules.system.vo.SysUserVo;
import com.ants.modules.system.vo.WxLogin;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;


/**
 * TODO
 * Author Chen
 * Date   2021/9/7 18:44
 */
@RestController
@RequestMapping("/sys")
@Api(tags = "用户登录")
@Slf4j
public class LoginController {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    ISysUserRoleService sysUserRoleServicel;
    @Autowired
    ISysRoleService sysRoleService;
    @Autowired
    ISysBaseAPI sysBaseAPI;


    private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";

    @AutoLog(value = "用户登陆", logType = CommonConstant.LOG_TYPE_1)
    @ApiOperation("登录接口")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Result<SysLoginModel> login(@RequestBody SysLoginModel sysLoginModel,
                                       HttpServletRequest req) {
        Result<SysLoginModel> result = new Result<SysLoginModel>();
        String username = sysLoginModel.getUsername();
        String password = sysLoginModel.getPassword();

        String captcha = sysLoginModel.getCaptcha();
        if (captcha == null) {
            result.error500("验证码无效");
            return result;
        }
        String lowerCaseCaptcha = captcha.toLowerCase();
        Object checkCode = redisUtil.get("captcha_" + sysLoginModel.getCheckKey());
        if (checkCode == null || !checkCode.toString().equals(lowerCaseCaptcha) ) {
            result.error500("验证码错误");
            return result;
        }
        //1. 校验用户是否有效
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, username);
        SysUser sysUser = sysUserService.getOne(queryWrapper);
        result = sysUserService.checkUserIsEffective(sysUser);
        if (!result.isSuccess()) {
            return result;
        }

        //2. 校验用户名或密码是否正确
        String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
        String syspassword = sysUser.getPassword();
        if (!syspassword.equals(userpassword)) {
            result.error500("用户名或密码错误");
            return result;
        }
        // 3. 登录
        StpUtil.login(sysLoginModel.getUsername());
        // 构建在线用户信息  储存 Redis
        genOnLineInfo(sysLoginModel, req, sysUser);

        result.setCode(200);
        result.setResult(sysLoginModel);
        return result;
    }

    @ApiOperation("微信接口")
    @AutoLog(value = "微信用户登陆", logType = CommonConstant.LOG_TYPE_1)
    @RequestMapping(value = "/wxLogin", method = RequestMethod.POST)
    public Result<?> wxLogin(@RequestBody WxLogin wxLogin,
                             HttpServletRequest req) {
        // 获取OpenId
        cn.hutool.json.JSONObject wxInfo = getWxOpenId(wxLogin.getJs_code());
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getOpenId, wxInfo.getStr("openid"));
        SysUser sysUser = sysUserService.getOne(queryWrapper);
        if (Objects.isNull(sysUser)) {
            wxLogin.setIsBind(false);
        } else {
            wxLogin.setIsBind(true);
            wxLogin.setSysUser(sysUser);
            SysLoginModel sysLoginModel = new SysLoginModel();
            sysLoginModel.setUsername(sysUser.getUsername());
            sysLoginModel.setPassword(sysUser.getPassword());
            StpUtil.login(sysUser.getUsername());
            // 设置在线用户
            genOnLineInfo(sysLoginModel, req, sysUser);
            wxLogin.setToken(sysLoginModel.getToken());
        }
        wxLogin.setOpenId(wxInfo.getStr("openid"));
        return Result.ok(wxLogin);
    }

    @ApiOperation("wxLoginTest")
    @RequestMapping(value = "/wxLoginTest", method = RequestMethod.POST)
    public Result<?> wxLoginTest(@RequestBody WxLogin wxLogin,
                                 HttpServletRequest req) {
        // 获取OpenId
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getOpenId, "oEeTV4uF_GrK5YxPCYH5Uwv6zBQY");
        SysUser sysUser = sysUserService.getOne(queryWrapper);
        if (Objects.isNull(sysUser)) {
            wxLogin.setIsBind(false);
        } else {
            wxLogin.setIsBind(true);
            wxLogin.setSysUser(sysUser);

            SysLoginModel sysLoginModel = new SysLoginModel();
            sysLoginModel.setUsername(sysUser.getUsername());
            sysLoginModel.setPassword(sysUser.getPassword());
            StpUtil.login(sysUser.getUsername());
            genOnLineInfo(sysLoginModel, req, sysUser);
            wxLogin.setToken(sysLoginModel.getToken());
        }
        wxLogin.setOpenId("oEeTV4uF_GrK5YxPCYH5Uwv6zBQY");
        return Result.ok(wxLogin);
    }

    @ApiOperation("微信用户绑定")
    @AutoLog(value = "微信用户绑定", logType = CommonConstant.LOG_TYPE_1)
    @RequestMapping(value = "/userBind", method = RequestMethod.POST)
    public Result<?> userBind(@RequestBody SysLoginModel sysLoginModel,
                              HttpServletRequest req) {
        Result<SysLoginModel> result = new Result<SysLoginModel>();
        String username = sysLoginModel.getUsername();
        String password = sysLoginModel.getPassword();

        //1. 校验用户是否有效
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, username);
        SysUser sysUser = sysUserService.getOne(queryWrapper);
        result = sysUserService.checkUserIsEffective(sysUser);
        if (!result.isSuccess()) {
            return result;
        }

        //2. 校验用户名或密码是否正确
        String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
        String syspassword = sysUser.getPassword();
        if (!syspassword.equals(userpassword)) {
            result.error500("用户名或密码错误");
            return result;
        }
        // 登录
        StpUtil.login(sysLoginModel.getUsername());
        // 设置在线用户
        genOnLineInfo(sysLoginModel, req, sysUser);

        WxLogin wxLogin = new WxLogin();
        sysUser.setOpenId(sysLoginModel.getOpenId());
        sysUserService.updateById(sysUser);
        wxLogin.setIsBind(true);
        wxLogin.setSysUser(sysUser);

        wxLogin.setToken(StpUtil.getTokenValue());
        return Result.ok(wxLogin);
    }

    private void genOnLineInfo(SysLoginModel sysLoginModel, HttpServletRequest req, SysUser sysUser) {
        String token = StpUtil.getTokenValue();
        sysLoginModel.setToken(token);
        String ip = IPUtils.getIpAddress(req);
        String browser = IPUtils.getBrowser(req);
        String ipAddress = IPUtils.getLocalCityInfo(ip);
        sysLoginModel.setBrowser(browser);
        sysLoginModel.setIp(ip);
        sysLoginModel.setIpAddr(ipAddress);
        sysLoginModel.setPassword(sysUser.getPassword());
        sysLoginModel.setName(sysUser.getRealname());
        sysLoginModel.setAvatar(sysUser.getAvatar());
        sysLoginModel.setLoginTime(new Date());
        redisUtil.set("onLine:" + token, sysLoginModel);

    }

    private static cn.hutool.json.JSONObject getWxOpenId(String code) {
        String appid = "wx5515300d09cbf231";
        String secret = "3dd88263d7156f526e26d6d13ead687a";
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="
                + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
        String result = HttpUtil.get(url);
        cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(result);
        System.out.println(jsonObject);
        return jsonObject;
    }

    @GetMapping("/info")
    @ApiOperation("/info")
    public Result info(HttpServletRequest request) {
        SysUserVo userVo = new SysUserVo();
        String loginId = StpUtil.getLoginIdAsString();
        LambdaQueryWrapper<SysUser> lqw = new LambdaQueryWrapper<>();
        lqw.eq(SysUser::getUsername, loginId);
        SysUser sysUser = sysUserService.getOne(lqw);
        BeanUtils.copyProperties(sysUser, userVo);
        // 查询用户角色
        List<String> roles = sysBaseAPI.getRoleNamesByUsername(loginId);
        userVo.setRoles(roles);
        return Result.ok(userVo);
    }

    /**
     * 退出登录
     *
     * @param request
     * @param response
     * @return
     */
    @AutoLog(value = "退出登录", logType = CommonConstant.LOG_TYPE_1)
    @RequestMapping(value = "/logout")
    public Result<Object> logout(HttpServletRequest request, HttpServletResponse response) {
        StpUtil.logout();
        return Result.ok("退出登录成功！");
    }


    /**
     * 后台生成图形验证码 ：有效
     *
     * @param response
     * @param key
     */
    @ApiOperation("获取验证码")
    @GetMapping(value = "/randomImage/{key}")
    public Result<?> randomImage(HttpServletResponse response, @PathVariable String key) {
        Result<String> res = new Result<String>();
        try {
            String code = RandomUtil.randomString(BASE_CHECK_CODES, 4);
            String lowerCaseCode = code.toLowerCase();
            String realKey = MD5Util.MD5Encode(lowerCaseCode + key, "utf-8");
            redisUtil.set(realKey, lowerCaseCode, 60);
            String base64 = RandImageUtil.generate(code);
            res.setSuccess(true);
            res.setResult(base64);
        } catch (Exception e) {
            res.error500("获取验证码出错" + e.getMessage());
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 后台生成图形验证码 ：有效
     *
     * @param response
     * @param key
     */
    @ApiOperation("获取验证码")
    @GetMapping(value = "/getCaptcha")
    public Result<?> getCaptcha() {
        //生成验证码对象
        SpecCaptcha captcha = new SpecCaptcha(130, 48, 5);
        //设置验证码的字符类型
        captcha.setCharType(Captcha.TYPE_DEFAULT);
        String key = UUID.randomUUID().toString();
        String verCode = captcha.text().toLowerCase();   // 获取验证码的字符
        log.info("验证码 Key:{} Vercode:{}", key, verCode);
        redisUtil.set("captcha_" + key, verCode, 60);
        Map<String, String> map = new HashMap<>();
        map.put("key", key);
        map.put("image", captcha.toBase64());
        return Result.ok(map);
    }


    /**
     * 图形验证码
     *
     * @param sysLoginModel
     * @return
     */
    @RequestMapping(value = "/checkCaptcha", method = RequestMethod.POST)
    public Result<?> checkCaptcha(@RequestBody SysLoginModel sysLoginModel) {
        String captcha = sysLoginModel.getCaptcha();
        String checkKey = sysLoginModel.getCheckKey();
        if (captcha == null) {
            return Result.error("验证码无效");
        }
        String lowerCaseCaptcha = captcha.toLowerCase();
        String realKey = MD5Util.MD5Encode(lowerCaseCaptcha + checkKey, "utf-8");
        Object checkCode = redisUtil.get(realKey);
        if (checkCode == null || !checkCode.equals(lowerCaseCaptcha)) {
            return Result.error("验证码错误");
        }
        return Result.ok();
    }

    @AutoLog(value = "注册用户", logType = CommonConstant.LOG_TYPE_1)
    @PostMapping("/register")
    public Result<?> register(@RequestBody JSONObject jsonObject) {
        SysUser user = JSON.parseObject(jsonObject.toJSONString(), SysUser.class);
        String captchaCode = (String) redisUtil.get("captchaCode:" + user.getEmail());
        if (StrUtil.isNotBlank(captchaCode)) {
            String captcha = (String) jsonObject.get("captcha");
            if (captchaCode.equals(captcha)) {
                user.setCreateTime(new Date()); //设置创建时间
                String salt = oConvertUtils.randomGen(8);
                user.setSalt(salt);
                String passwordEncode = PasswordUtil.encrypt(user.getUsername(), user.getPassword(), salt);
                user.setPassword(passwordEncode);
                user.setStatus(1);
                user.setDelFlag(CommonConstant.DEL_FLAG_0);
                user.setCreateBy("用户注册");
                user.setCreateTime(new Date());
                sysUserService.addUserWithRole(user, "1446724729018728450");
            } else {
                return Result.error("验证码错误！");
            }
        } else {
            return Result.error("验证码已过期，请重新发送！");
        }
        return Result.ok("添加成功！");
    }


    @AutoLog(value = "微信注册用户", logType = CommonConstant.LOG_TYPE_1)
    @PostMapping("/wxRegister")
    public Result<?> wxRegister(@RequestBody JSONObject jsonObject) {
        SysUser user = JSON.parseObject(jsonObject.toJSONString(), SysUser.class);
        String salt = oConvertUtils.randomGen(8);
        user.setSalt(salt);
        String passwordEncode = PasswordUtil.encrypt(user.getUsername(), user.getPassword(), salt);
        user.setPassword(passwordEncode);
        user.setStatus(1);
        user.setDelFlag(CommonConstant.DEL_FLAG_0);
        user.setCreateBy("用户注册");
        user.setCreateTime(new Date());
        user.setOpenId("");
        sysUserService.addUserWithRole(user, "1446724729018728450");

        return Result.ok("注册成功！");
    }

    @GetMapping("/renewalToken")
    public Result<?> renewalToken() {
        StpUtil.updateLastActivityToNow();
        return Result.ok();
    }
}
